package cn.xinfei.xdecision.engine.runner.executor.strategy;

import cn.xinfei.xdecision.common.model.component.strategyout.StrategyOutput;
import cn.xinfei.xdecision.common.model.datax.consts.CommonConstant;
import cn.xinfei.xdecision.common.model.datax.enums.VariableEnum;
import cn.xinfei.xdecision.common.model.enginex.model.*;
import cn.xinfei.xdecision.common.model.enginex.po.EngineNode;
import cn.xinfei.xdecision.common.utils.constant.SysConstant;
import cn.xinfei.xdecision.common.utils.constant.runner.ParamTypeConst;
import cn.xinfei.xdecision.common.utils.util.runner.JevalUtil;
import cn.xinfei.xdecision.common.vo.FieldInfoVo;
import cn.xinfei.xdecision.engine.runner.context.AmmunitionDepot;
import cn.xinfei.xdecision.engine.runner.context.PipelineContext;
import cn.xinfei.xdecision.engine.runner.context.PipelineContextHolder;
import cn.xinfei.xdecision.engine.runner.context.XDecisionContext;
import cn.xinfei.xdecision.engine.runner.datax.RiskDataApiAdapter;
import cn.xinfei.xdecision.engine.runner.executor.NodeHandler;
import cn.xinfei.xdecision.engine.runner.executor.model.EngineNodeLog;
import cn.xinfei.xdecision.engine.runner.executor.model.StateTransmitModel;
import cn.xinfei.xdecision.engine.runner.producer.DecisionProducer;
import cn.xinfei.xdecision.nacos.NacosSwitchProperties;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * 引擎节点执行
 */
@Data
@Slf4j
public abstract class EngineNodeRunner extends EngineRunner<NodeHandler> {

    public static final String PRE = "Node_";

    @Autowired
    private DecisionProducer decisionProducer;

    @Autowired
    private NacosSwitchProperties nacosSwitchProperties;

    @Override
    public void before(NodeHandler nodeHandler) {

    }


    /**
     * 终止条件判断
     *
     * @param nodeHandler
     * @param
     */
    @Override
    public boolean terminalCondition(NodeHandler nodeHandler, Map<String, Object> nodeOutput, Map<String, Object> nodeInput) {
        EngineNode engineNode = nodeHandler.getEngineNode();
        String engineCode = engineNode.getEngineCode();
        String nodeCode = engineNode.getNodeCode();
        boolean result = false;
        String conditions = null;
        try {

            //判断终止条件是否满足，满足则结束
            if (org.apache.commons.lang.StringUtils.isBlank(engineNode.getNodeScript())) {
                return false;
            }

            NodeScriptModel nodeScriptModel = JSONObject.parseObject(engineNode.getNodeScript(), NodeScriptModel.class);
            TerminationInfo terminationInfo = nodeScriptModel.getTerminationInfo();
            if (null == terminationInfo) {
                return false;
            }
            List<SelectedRule> selectedRule = terminationInfo.getSelectedRule();
            if (selectedRule == null || selectedRule.isEmpty()) {
                return false;
            }
            conditions = terminationInfo.getScriptConditions();
            Map<String, Integer> fieldTypeMap = terminationInfo.getFieldTypeMap();
            //终止条件判断是基于当前node的输出，如果传context可能性能不好，后面优化
            Map<String, Object> contextValue = PipelineContextHolder.getContextValue();
            // 判断终止条件
            Map<String, Object> terminalInputs = AmmunitionDepot.convertVariables(fieldTypeMap, contextValue);
            //把终止输出中的输入变量存入输入变量集合
            nodeInput.putAll(terminalInputs);
//            result = JevalUtil.evaluateBoolean(conditions, contextValue);
            log.info("terminalInputs={}",JSONObject.toJSONString(terminalInputs));
            result = JevalUtil.evaluateBoolean(conditions, terminalInputs);

            log.info("是否命中终止条件,result={},nodeName={},nodeCode={}，conditions={}", result, engineNode.getNodeName(), nodeCode, conditions);

            //此处以下是节点维度的，终止输出
            if (result) {
                List<NodeOutPut> allOutPuts = terminationInfo.getAllOutPuts();

                //装配输出变量
                for (NodeOutPut outPut : allOutPuts) {
                    String fieldValue = outPut.getValue();
                    String fieldCode = outPut.getFieldCode();
                    int variableType = outPut.getValueType();

                    Object outValue = null;
                    switch (variableType) {
                        case ParamTypeConst.CONSTANT:
                            //常量类型
                            outValue = AmmunitionDepot.getValueByKeyFromRemote(variableType, fieldCode, fieldValue + "");
                            break;
                        case ParamTypeConst.VARIABLE:
                            //变量类型
                            outValue = AmmunitionDepot.getValueByKeyFromRemote(variableType, fieldValue, fieldValue + "");
                            break;
                    }

                    if (null != outValue) {
                        //上下文
                        setOutput(nodeHandler, fieldCode, outValue);
                        //节点输出
                        nodeOutput.put(fieldCode, outValue);
                    }
                }


                //判断规则节点中，命中结果里是否有reject和pass，基于这个值对终止条件，进行重置
                NodeOutPut terminationResult = terminationInfo.getTerminationResult();
                if (null != terminationResult) {
                    String terminationFieldCodeValue = terminationResult.getValue();
                    result = !terminationFieldCodeValue.equalsIgnoreCase(CommonConstant.STATUS_PASS);
                }
            }
        } catch (Exception e) {
            log.error("终止条件执行异常,执行内容：conditions={},terminalCondition fail.error={},engineCode={},nodeCode={}",
                    conditions, e.getMessage(), engineCode, nodeCode, e);
        }
        return result;
    }


    /**
     * 自定义输出配置里有可能存在变量伏值，该方法是从输出配置里提取出需要输入的 变量
     *
     * @param nodeHandler
     * @return
     */
    @Override
    public Map<String, FieldInfoVo> getInputFieldInfo(NodeHandler nodeHandler) {
        Map<String, FieldInfoVo> in = new HashMap<>();
        EngineNode engineNode = nodeHandler.getEngineNode();
        String nodeCode = engineNode.getNodeCode();
        String engineCode = engineNode.getEngineCode();
        try {

            String nodeNodeJson = engineNode.getNodeJson();
            if (StringUtils.isEmpty(nodeNodeJson)) {
                log.debug("node json is null.engineCode={},nodeCode={}", engineCode, nodeCode);
                return in;
            }
            if (engineCode.equals("jcl_20230621000003")) {
                System.out.println();
            }

            Map<String, FieldInfoVo> fieldInfo = metadataProvider.getFieldInfo();
            NodeJsonModel nodeJsonModel = JSONObject.parseObject(nodeNodeJson, NodeJsonModel.class);
            List<EngineNodeModel> componentList = nodeJsonModel.getComponentList();
            String nodeType = engineNode.getNodeType();
            //1.自定义输出中引用的变量
            if (null != componentList && componentList.size() > 0 && fieldInfo.size() > 0) {
                componentList.forEach(component -> {
                    Long versionId = component.getVersionId();
                    StrategyOutput strategyOutput = new StrategyOutput(versionId, nodeType);
                    List<StrategyOutput> outputList = strategyOutputService.list(new QueryWrapper<>(strategyOutput));
                    outputList.stream().filter(out -> out.getValueType() == 2).forEach(out -> {

                        FieldInfoVo fieldInfoVo = fieldInfo.get(out.getFieldValue());
                        in.put(fieldInfoVo.getFieldCode(), fieldInfoVo);
                    });
                });
            }
            //2.节点中存在的判断条件，也是输入变量的一种
            Map<String, FieldInfoVo> terminalInputFieldInfo = getTerminalInputFieldInfo(nodeHandler);
            in.putAll(terminalInputFieldInfo);
        } catch (Exception e) {
            log.error("getInputFieldInfo:获取指标异常,error={},nodeCode={}", e.getMessage(), nodeCode, e);
        }
        return in;
    }

    /**
     * //2.节点中存在的判断条件，也是输入变量的一种
     *
     * @param nodeHandler
     * @return
     */
    @Override
    public Map<String, FieldInfoVo> getTerminalInputFieldInfo(NodeHandler nodeHandler) {
        Map<String, FieldInfoVo> in = new HashMap<>();
        EngineNode engineNode = nodeHandler.getEngineNode();
        String nodeCode = engineNode.getNodeCode();
        String engineCode = engineNode.getEngineCode();
        try {

            String nodeScript = engineNode.getNodeScript();
            if (StringUtils.isEmpty(nodeScript)) {
                log.debug("nodeScript is null.engineCode={},nodeCode={}", engineCode, nodeCode);
                return in;
            }

            Map<String, FieldInfoVo> fieldInfo = metadataProvider.getFieldInfo();
            NodeScriptModel nodeScriptModel = JSONObject.parseObject(nodeScript, NodeScriptModel.class);
            TerminationInfo terminationInfo = nodeScriptModel.getTerminationInfo();
            if (null != terminationInfo) {
                Map<String, Integer> fieldTypeMap = terminationInfo.getFieldTypeMap();
                if (null != fieldTypeMap && fieldTypeMap.size() > 0) {
                    fieldTypeMap.entrySet().forEach(entry -> {
                        Integer valueType = entry.getValue();
                        String fieldCode = entry.getKey();
                        FieldInfoVo fieldInfoVo = fieldInfo.get(fieldCode);
                        in.put(fieldInfoVo.getFieldCode(), fieldInfoVo);

                    });
                }
            }
        } catch (Exception e) {
            log.error("getTerminalInputFieldInfo:获取指标异常,error={},nodeCode={}", e.getMessage(), nodeCode, e);
        }
        return in;
    }

    @Override
    public Map<String, StrategyOutput> getTerminalOutputFieldInfo(NodeHandler nodeHandler) {
        EngineNode engineNode = nodeHandler.getEngineNode();
        Map<String, StrategyOutput> outputResult = new HashMap<>();
        String nodeCode = engineNode.getNodeCode();
        String engineCode = engineNode.getEngineCode();
        Map<String, FieldInfoVo> fieldInfo = metadataProvider.getFieldInfo();
        String nodeNodeJson = engineNode.getNodeJson();
        if (StringUtils.isEmpty(nodeNodeJson)) {
            log.debug("nodeScript is null.engineCode={},nodeCode={}", engineCode, nodeCode);
            return outputResult;
        }
        NodeJsonModel nodeJsonModel = JSONObject.parseObject(nodeNodeJson, NodeJsonModel.class);
        //节点自定义终止输出
        TerminationInfo terminationInfo = nodeJsonModel.getTerminationInfo();
        if (null != terminationInfo) {
            List<NodeOutPut> allOutPuts = terminationInfo.getAllOutPuts();
            for (NodeOutPut outPut : allOutPuts) {
                String fieldCode = outPut.getFieldCode();
                FieldInfoVo fieldInfoVo = fieldInfo.get(fieldCode);
                if (fieldInfoVo != null) {
                    StrategyOutput strategyOutput = new StrategyOutput();
                    strategyOutput.setFieldCode(fieldCode);
                    strategyOutput.setFieldValue(outPut.getValue());
                    strategyOutput.setVarValueType(fieldInfoVo.getValueType());
                    strategyOutput.setValueType(outPut.getValueType());
                    strategyOutput.setStage(fieldInfoVo.getStage());
                    outputResult.put(fieldCode, strategyOutput);
                } else {
                    log.error("变量中心不存在此变量,fieldCode={}", fieldCode);
                }
            }
        }
        return outputResult;
    }

    @Override
    public Map<String, StrategyOutput> getOutputFieldInfo(NodeHandler nodeHandler) {
        EngineNode engineNode = nodeHandler.getEngineNode();
        Map<String, StrategyOutput> outputResult = new HashMap<>();
        String nodeCode = engineNode.getNodeCode();
        String engineCode = engineNode.getEngineCode();
        try {

            String nodeNodeJson = engineNode.getNodeJson();
            if (StringUtils.isEmpty(nodeNodeJson)) {
                log.debug("node json is null.engineCode={},nodeCode={}", engineCode, nodeCode);
                return outputResult;
            }

            Map<String, FieldInfoVo> fieldInfo = metadataProvider.getFieldInfo();
            NodeJsonModel nodeJsonModel = JSONObject.parseObject(nodeNodeJson, NodeJsonModel.class);
            //组件自定义输出
            List<EngineNodeModel> componentList = nodeJsonModel.getComponentList();
            String nodeType = engineNode.getNodeType();
            if (null != componentList && componentList.size() > 0 && fieldInfo.size() > 0) {
                componentList.forEach(component -> {
                    Long versionId = component.getVersionId();
                    StrategyOutput strategyOutput = new StrategyOutput(versionId, nodeType);
                    List<StrategyOutput> outputList = strategyOutputService.list(new QueryWrapper<>(strategyOutput));
                    outputList.stream().forEach(out -> {
                        FieldInfoVo fieldInfoVo = fieldInfo.get(out.getFieldCode());
                        out.setVarValueType(fieldInfoVo.getValueType());
                        out.setStage(fieldInfoVo.getStage());
                        outputResult.put(out.getFieldCode(), out);
                    });
                });
            }

            /**
             * 节点终止输出
             */
            /*Map<String, StrategyOutput> terminalOutputFieldInfo = getTerminalOutputFieldInfo(nodeHandler);
            outputResult.putAll(terminalOutputFieldInfo);*/

        } catch (Exception e) {
            log.error("getOutputFieldInfo:获取指标异常,error={},nodeCode={}", e.getMessage(), nodeCode, e);
        }
        return outputResult;
    }


    @Override
    public void getNodeFieldValue(NodeHandler nodeHandler) throws Exception {
//        if (nacosSwitchProperties.isPreloadNode() == false) {
//            log.info("不进行节点预加载");
//            return;
//        }

        EngineNode engineNode = nodeHandler.getEngineNode();
        String nodeCode = engineNode.getNodeCode();
        Set<String> fieldSet = new HashSet<>();
        Map<String, FieldInfoVo> inputFields = nodeHandler.getInputFields();
        if (inputFields != null && !inputFields.isEmpty()) {
            fieldSet.addAll(inputFields.keySet());
        }
        //查询当前决策流的所有接口变量，sql变量，衍生变量，并放入fieldSet里调dataServer服务
        String fieldCodes = nodeHandler.getEngineNode().getFieldCodes();
        if (StringUtils.isNotEmpty(fieldCodes)) {
            String[] varCodes = fieldCodes.split(SysConstant.COMMA);
            for (String varCode : varCodes) {
                FieldInfoVo fieldInfoVo = metadataProvider.getFieldInfo().get(varCode);
                if (Objects.nonNull(fieldInfoVo) && (
                        Objects.equals(fieldInfoVo.getVarTypeId(), VariableEnum.DATABASE_VAR.getCode()) ||
                                Objects.equals(fieldInfoVo.getVarTypeId(), VariableEnum.DERIVE_VAR.getCode()) ||
                                Objects.equals(fieldInfoVo.getVarTypeId(), VariableEnum.INTERFACE_VAR.getCode())
                )) {
//                    log.info("装配接口变量，varCode==>{}", varCode);
                    fieldSet.add(varCode);
                }
            }
        }
        getNodeFieldValue(fieldSet, nodeCode);
    }


    public void getNodeFieldValue(Set<String> fieldSet, String nodeCode) throws Exception {

        try {
            if (CollectionUtils.isNotEmpty(fieldSet)) {
                //剔除掉上下文的变量
                Set<String> contextVarCodes = XDecisionContext.getContext().getPipelineContext().getContext().keySet();
                fieldSet.removeAll(contextVarCodes);
//                log.info("剔除掉上下文的fieldSet==>{}", fieldSet);
                Set<String> fetchField = new HashSet<>();
                fieldSet.forEach(k -> {
                    if (PipelineContextHolder.containsKey(k)) {
                        Object valueByKey = PipelineContextHolder.getValueByKey(k);
//                        Object valueByKey = AmmunitionDepot.getValueByKey(k);
                        if (valueByKey == null || valueByKey.toString().equals("") || valueByKey.toString().equals("null")) {
                            fetchField.add(k);//对于不存在的，等于空串的，等于"null"的都会重新发起调用
                        }
                    } else {
                        fetchField.add(k);
                    }
                });
                log.info("调用实时加载接口,fetchField={},nodeCode={}", fetchField, nodeCode);
                dataRealCollectHandler.queryFieldValue(fetchField, RiskDataApiAdapter.CollectTypeEnum.REAL.toString());
            } else {
                log.warn("无需调用实时接口，inputFields 参数为null,nodeCode={}", nodeCode);
            }
        } catch (Exception e) {
            log.error("getNodeFieldValue from data server failed. error={},nodeCode={}", e.getMessage(), nodeCode, e);
            throw e;
        }
    }

    @Override
    public void after(NodeHandler nodeHandler, StateTransmitModel stateTransmitModel, Map<String, Object> nodeOutput, Map<String, Object> nodeInput) {

        Map<String, Object> ruleNodeOutput = stateTransmitModel.getNodeOutput();
        Map<String, Object> ruleNodeInput = stateTransmitModel.getNodeInput();

        ruleNodeOutput.putAll(nodeOutput);
        ruleNodeInput.putAll(nodeInput);

        if (PipelineContextHolder.isPersistence() == false) {
            log.info("测试数据不发起持久化");
            return;
        }

        String decisionId = PipelineContextHolder.getDecisionId();
        String requestId = PipelineContextHolder.getRequestId();
        String engineCode = PipelineContextHolder.getEngineCode();
        EngineNode engineNode = nodeHandler.getEngineNode();
        String nodeCode = engineNode.getNodeCode();

        EngineNodeLog engineNodeLog = new EngineNodeLog();
        engineNodeLog.setNodeName(engineNode.getNodeName());
//        engineNodeLog.setVersionNo(engineNode.get());
        engineNodeLog.setInput(ruleNodeInput);
        engineNodeLog.setOutput(ruleNodeOutput);
        engineNodeLog.setRequestId(requestId);
        engineNodeLog.setDecisionId(decisionId);
        engineNodeLog.setEngineCode(engineCode);
        engineNodeLog.setNodeCode(nodeCode);
        engineNodeLog.setNodeType(nodeHandler.getNodeType());
        //这两个字段不再继承，改为单独存储当前节点的输出
//        engineNodeLog.setRuleHitList(PipelineContextHolder.getHitResultList());
//        engineNodeLog.setBranchRejectInfo(PipelineContextHolder.getBranchRejectInfo());
        engineNodeLog.setRuleHitList(stateTransmitModel.getHitResultList());
        engineNodeLog.setBranchRejectInfo(stateTransmitModel.getBranchRejectInfo());
        decisionProducer.sendNodeLog(engineNodeLog);
    }

    /**
     * 清理掉取默认值的变量: _变量名:null
     */
    @Override
    public void clear() {
        StateTransmitModel stateTransmitModel = XDecisionContext.getContext().getStateContext();
        stateTransmitModel.getNodeInput().clear();
        stateTransmitModel.getNodeOutput().clear();
        stateTransmitModel.getHitResultList().clear();
        stateTransmitModel.getBranchRejectInfo().clear();

        ConcurrentMap<String, Object> contextValue = PipelineContextHolder.getContextValue();
        Set<Map.Entry<String, Object>> entries = contextValue.entrySet();
        Iterator<Map.Entry<String, Object>> iterator = entries.iterator();
        Set<String> defaultVariableSet = new HashSet<>();
        while (iterator.hasNext()) {
            Map.Entry<String, Object> next = iterator.next();
            String key = next.getKey();
            boolean b = key.startsWith("_");
            if (b) {
                defaultVariableSet.add(key);
                defaultVariableSet.add(key.substring(1));
            }
        }
        if (defaultVariableSet.size() > 0) {
            log.info("clear default variables={}", defaultVariableSet);
            XDecisionContext xDecisionContext = XDecisionContext.getContext();
            PipelineContext pipelineContext = xDecisionContext.getPipelineContext();
            ConcurrentHashMap<String, Object> context = pipelineContext.getContext();
            defaultVariableSet.forEach(k -> {
                context.remove(k);
            });
        }

    }
}
